#include <stdio.h>
#include <stdlib.h>
#include "dominion.h"
#include "rngs.h"
#include "math.h"
#include <assert.h>
#include <string.h>

/*
Adventurer - Card type - Action
Reveal cards from your deck until you reveal 2 treasure cards.
Put those treasure cards in your hand and discard the other reavealed cards.
Additional Rules - If you have to shuffle in the middle, shuffle.
Don't shuffle in the revealed cards as these cards do not go to the
discard pile until you ahve finished revealing cards.  If you run out
of cards after shuffling and only have one treasure, you just get the
one treasure.

pre-requirements - adventurer card must be in handpos

post-requirements: 
1. cards in (hand + discard + deck) must be the same as before, but may be re-arranged
2. One less adventurer card should be in the hand
	Buys should not change
	Actions should not change
3. If two or more treasure cards were in deck: the two in highest pos must now be in hand,
and anything in higher pos must be in discard
4. If only one treasure card is was in deck: it must now be in hand,
and all other cards from deck must be in discard.
	3.a. If (above)&& at least one treasure card is was in discard then one of them
	must be in the hand
		3.a-1 If no treasure was in discard then the deck should be empty
5. If no treasure card was in the deck: Deck should be empty
	4.a If two or more treasure cards were in discard: two new T cards must be in hand
	4.b If one treasure card was in discard: one new T card must be in hand
	4.c If no T cards were in discard: T cards in hand before = after


*/
int MAX_SUPPLY = 27;
void prepRandom(int strm, int seed){
  SelectStream(strm);
  PutSeed((long)seed);
}

int getRandomNumber(int min, int max){
	int number = floor(Random() * ((max-min)+1));
	number += min;
	if(number > max){
		number = max;
	}
	return number;

}




void randomizeSupply(struct gameState *g){
	int i;
	for(i=0; i < MAX_SUPPLY; i++){
		g->supplyCount[i] = getRandomNumber(0, 40);
	}
}

void randomizeHands(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->handCount[i] = getRandomNumber(0, MAX_HAND);
		for(j = 0; j < g->handCount[i]; j++){
			g->hand[i][j] = getRandomNumber(0, MAX_SUPPLY);
			if(g->hand[i][j] == MAX_SUPPLY) g->hand[i][j] = 26;
		}

	}
}

void randomizeDecks(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->deckCount[i] = getRandomNumber(0, MAX_DECK);
		for(j = 0; j < g->deckCount[i]; j++){
			g->deck[i][j] = getRandomNumber(0, MAX_SUPPLY);
			if(g->deck[i][j] == MAX_SUPPLY) g->deck[i][j] = 26;
		}

	}
	
}

void randomizeDiscard(struct gameState *g){
	int players = g->numPlayers;
	int i, j;
	for(i=0; i < players; i++){
		g->discardCount[i] = getRandomNumber(0, MAX_DECK);
		for(j = 0; j < g->discardCount[i]; j++){
			g->discard[i][j] = getRandomNumber(0, 27);
			if(g->discard[i][j] == MAX_SUPPLY) g->discard[i][j] = 26;
		}

	}
	
}
void randomizeTurn(struct gameState *g){
	g->whoseTurn = getRandomNumber(0, g->numPlayers);
}

//randomizeTurn() should be called before calling this


int makeRandomGameState(struct gameState *g){
//	printf("Randomizing Game\n");
	g->numPlayers = getRandomNumber(2, 5);
	if(g->numPlayers == 5) g->numPlayers = 4;
	randomizeSupply(g);
	randomizeDecks(g);
	randomizeDiscard(g);
	randomizeHands(g);
	randomizeTurn(g);
	g->coins = getRandomNumber(0, 20);
	g->numActions = getRandomNumber(0, 5);
	g->numBuys = getRandomNumber(1, 5);
//	printf("Game Randomized\n");
	return 0;
}

//returns an array which contains how many of each card are in
// hand(1), deck(2), discard(3), or all(4)
//
int *countAllCards(struct gameState *g, int which){
	assert(which > 0 && which < 5);
	int whoseTurn = g->whoseTurn;
	int deckCount = g->deckCount[whoseTurn];
	int discardCount = g->discardCount[whoseTurn];
	int handCount = g->handCount[whoseTurn];
	int* allCards = malloc(sizeof(int) * MAX_SUPPLY);

	int i, j;
	for(i=0; i<MAX_SUPPLY; i++){
		allCards[i] = 0;  //just to be sure
	}
	for (i=0; i<MAX_SUPPLY; i++){
		if(which == 1 || which == 4){
			for(j=0; j < handCount; j++){
				if (g->hand[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 2 || which == 4){
			for(j=0; j < deckCount; j++){
				if (g->deck[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
		if(which == 3 || which == 4){
			for(j = 0; j< discardCount; j++){
				if(g->discard[whoseTurn][j] == i){
					allCards[i]++;
				}
			}
		}
	}
	return allCards;
}

//returns how many adventurer cards are in hand
int AdventCardsInHand(struct gameState *g){
	int i;
	int count = 0;
	for(i=0; i < g->handCount[g->whoseTurn]; i++){
		if(g->hand[g->whoseTurn][i] == adventurer){
			count ++;
		}
	}
	return count;
}


//counts the number of cards that should be taken from the deck
//and added to the discard pile
int countCardstoDiscard(struct gameState *g){
	int treasureCount = 0;
	int nonTreasureCount = 0;
	int i = g->deckCount[g->whoseTurn] -1;
	while(i >= 0){
		if(g->deck[g->whoseTurn][i] > 3 && g->deck[g->whoseTurn][i] < 7){
			treasureCount ++;
		}
		else{
			nonTreasureCount ++;
		}
		if(treasureCount == 2){
			return nonTreasureCount;
		}
		i--;
	}
	return nonTreasureCount;
}

//returns how many coin cards are in the current player's hand
int moneyCardsinHand(struct gameState *g){
	int mCards = 0;
	int handCount = g->handCount[g->whoseTurn];
	int i;
	for(i=0; i < handCount; i++){
		if(g->hand[g->whoseTurn][i] > 3 && g->hand[g->whoseTurn][i] < 7){
			mCards ++;
		}
	}
	return mCards;
}
int moneyCardsinDeck(struct gameState *g){
	int mCards = 0;
	int deckCount = g->deckCount[g->whoseTurn];
	int i;
	for(i=0; i < deckCount; i++){
		if(g->deck[g->whoseTurn][i] > 3 && g->deck[g->whoseTurn][i] < 7){
			mCards ++;
		}
	}
	return mCards;
}
int moneyCardsinDiscard(struct gameState *g){
	int mCards = 0;
	int discardCount = g->discardCount[g->whoseTurn];
	int i;
	for(i=0; i < discardCount; i++){
		if(g->discard[g->whoseTurn][i] > 3 && g->discard[g->whoseTurn][i] < 7){
			mCards ++;
		}
	}
	return mCards;
}

//returns 1 if 2 or more treasure cards are in deck
//returns 2 if 1 treasure card is in deck and at least one is in discard
//returns 3 if 1 treasure is in deck, and 0 are in discard
//returns 4 if 0 in deck and 2 in discard
//returns 5 if 0 in deck and 1 in discard
//returns 6 if 0 in deck and 0 in discard
int getTreasureStatus(struct gameState *g){
	int inDeck = moneyCardsinDeck(g);
	int inDiscard = moneyCardsinDiscard(g);
	if(inDeck > 1)
		return 1;
	else if(inDeck == 1 && inDiscard > 0)
		return 2;
	else if(inDeck == 1 && inDiscard == 0)
		return 3;
	else if(inDeck == 0 && inDiscard > 1)
		return 4;
	else if(inDeck ==0 && inDiscard == 1)
		return 5;
	else
		return 6;
}


int searchForMoney(struct gameState *g){
	int i;
	for(i=0; i < g->deckCount[g->whoseTurn]; i++){
		if(g->deck[g->whoseTurn][i] > 3 && g->deck[g->whoseTurn][i] < 7){
			printf("Found Money in Deck!\n");
			return 1;  //found money!
		}
	}
	for(i=0; i < g->discardCount[g->whoseTurn]; i++){
		if(g->discard[g->whoseTurn][i] > 3 && g->discard[g->whoseTurn][i] < 7){
			printf("Found Money in Discard!\n");
			return 1;  //found money!
		}
	}
	return 0; //didn't find any
}

//compares number of cards from two sets
//returns 1 if equal, 0 if not
int compareSet(int *set1, int *set2){
	int i;
	int pass = 1;
	for(i=0; i<MAX_SUPPLY; i++){
		if(set1[i] != set2[i]){
			//printf("card count for %d: set1: %d set2: %d\n", i, set1[i],set2[i]);
			//return 0;
			pass = 0;
		}
	}
	if (pass == 1)
		return 1;
	else
		return 0;
}

int testAdventurer(struct gameState *g, int *testFailure){
	//int coins = g->coins;
	int actions = g->numActions;
	int buys = g->numBuys;
	
	//make sure there is at least one card in the hand and card being played is in it.
	int handCount = g->handCount[g->whoseTurn];
	if(handCount <= 0){
		handCount = 1;
		g->handCount[g->whoseTurn] = handCount;
	}
	int handPos = getRandomNumber(0, (handCount - 1));
	g->hand[g->whoseTurn][handPos] = adventurer;
	
	//BEFORE
	int tStatus = getTreasureStatus(g);
	int discardFromDeck = countCardstoDiscard(g);
	int cardsInDiscardBefore = g->discardCount[g->whoseTurn];
//	int cardsInDeckBefore = g->deckCount[g->whoseTurn];

	int aInHandBefore = AdventCardsInHand(g);
	int mCardsBefore = moneyCardsinHand(g);
	int *cardsBefore = countAllCards(g, 4);
	//randomize choices
	int choice1 = getRandomNumber(0, 5);
	int choice2 = getRandomNumber(0, 5);
	int choice3 = getRandomNumber(0, 5);
	
	struct gameState cpy;
	struct gameState *copy = &cpy;

	memcpy(copy, g, sizeof(struct gameState));

	int b = 0;
	int *bonus = &b;
	int result = cardEffect(adventurer, choice1, choice2, choice3, g, handPos, bonus);
	if(result == -1){
		if(memcmp(copy, g, sizeof(struct gameState)) != 0){
			printf("Gamestate changed on negative result\n");
			testFailure[0]++;
		}
		return -1;
	}

	//AFTER
	int *cardsAfter = countAllCards(g, 4);

	//test for requirement 1.  Failing about 8/100 times
	if(!compareSet(cardsBefore, cardsAfter)){
		printf("Card sets are different!\n");
		testFailure[1]++;
	}
	//test for requirement 2. fails every time
	int aInHandAfter = AdventCardsInHand(g);
	if(aInHandAfter != aInHandBefore -1){
		testFailure[2]++;
		//printf("Adventurer card not discarded correctly. before: %d after: %d\n", aInHandBefore, aInHandAfter);
	}
	if(g->numActions != (actions)){
		printf("number of actions changed incorrectly!\n");
		testFailure[3]++;
	}
	if(g->numBuys != buys){
		printf("number of buys changed!\n");
		testFailure[3]++;
	}

	//test for requirement 3



	int mCardsAfter = moneyCardsinHand(g);
	if(tStatus ==1){
		if(cardsInDiscardBefore + discardFromDeck != g->discardCount[g->whoseTurn]){
				printf("Discard before: %d to be discarded: %d discard after: %d\n",cardsInDiscardBefore, discardFromDeck,g->discardCount[g->whoseTurn]);
				testFailure[4]++;
			}
			else{
			//	printf("deck cards properly discarded\n");
			}
	}
	
	if(mCardsAfter != mCardsBefore +2){
		if(tStatus == 1){
			printf("Two treasure cards were in deck and not added to hand\n");
			testFailure[5]++;
		}
		if(tStatus == 2){
			printf("One T card in deck and one in discard, not added to hand\n");
			testFailure[5]++;
		}
		if(tStatus == 3){
			if(mCardsAfter != mCardsBefore +1){
				printf("1 T card in deck, 0 in discard: not added to hand\n");
				testFailure[5]++;
			}
		}
		if(tStatus == 4){
			printf("0 in deck, 2 or more in discard: not added to hand\n");
			testFailure[5]++;
		}
		if(tStatus == 5){
			if(mCardsAfter != mCardsBefore +1){
				printf("0 in deck, 1 in discard: not added to hand\n");
				testFailure[5]++;
			}
		}
		if(tStatus == 6){
			if(mCardsAfter != mCardsBefore){
				printf("0 in deck, 0 in discard: hand has more or less\n");
				testFailure[5]++;
			}
		}
	}
	


	/*
	if(mCardsAfter < (mCardsBefore + 2)){
		if(searchForMoney(g) == 1){
			printf("Coin cards not added correctly\n");
		}
		
	}*/
	if(mCardsAfter > (mCardsBefore +2)){
		printf("Too many coin cards added!\n");
		testFailure[6]++;
	}


	return 1;
}

int main(int argc, char** argv){
	int strm = 1, seed = 4, min = 2, max = 50;
	if(argc > 1){
		strm = atoi(argv[1]);
	}
	if(argc > 2){
		seed = atoi(argv[2]);
	}
	if(argc > 3){
		min = atoi(argv[3]);
	}
	if(argc > 4){
		max = atoi(argv[4]);
	}
	prepRandom(strm, seed);
	int tests = 7;
	int testFailure[tests];

	int i;
	for(i=0; i<tests; i++){
		testFailure[i] = 0;
	}
	struct gameState *g = newGame();
	printf("Testing Adventurer.  It should add coin cards to the hand\n");
	int testRuns = 1000;
	printf("Iterating %d times\n", testRuns);
	for(i=0; i < testRuns; i++){
	
		makeRandomGameState(g);
		testAdventurer(g, testFailure);
	}
	for(i=0; i<tests; i++){
		printf("Test %d failed %d times\n", i, testFailure[i]);
	}
	printf("Test Complete\n");

	return 0;
}
